
#include "Timers.h"

C_Timer_Ptr
C_Timer_New_From(const C_Timer_Ptr t)
{
	C_Timer_Ptr p = C_Malloc(sizeof(C_Timer));
	C_Timer_Init_From(p, t);
	return p;
}

void
C_Timer_Init(C_Timer_Ptr p, long duration, C_Timer_Handler_t f, C_Bool_t periodic)
{
	if (p == NULL)
		return;
	p->Handler = f;
	p->Duration = duration;
	p->UserId = 0;
	p->Periodic = periodic;
	p->Id = C_TIMER_ID_INVALID;
	p->Timestamp.tv_sec = 0;
	p->Timestamp.tv_usec = 0;
}

void
C_Timer_Init_From(C_Timer_Ptr p, const C_Timer_Ptr t)
{
	if (p == NULL || t == NULL)
		return;
	p->Handler = t->Handler;
	p->Duration = t->Duration;
	p->Periodic = t->Periodic;
	p->UserId = t->UserId;
	p->Timestamp.tv_sec = t->Timestamp.tv_sec;
	p->Timestamp.tv_usec = t->Timestamp.tv_usec;
	p->Id = t->Id;
}

void
C_Timer_Set_User_Data(C_Timer_Ptr p, void *c, unsigned int id)
{
	if (p != NULL)
	{
		p->User = c;
		p->UserId = id;
	}
}

void
C_Timer_Manager_Init(C_Timer_Manager_Ptr p)
{
	C_Thread_Init((C_Thread_Ptr)p, (C_Thread_Run_t)C_Timer_Manager_Run);
	C_THREAD_SET_CLEANUP(p, C_Timer_Manager_Cleanup);
	p->Id = C_TIMER_ID_INVALID;
	p->Mutex = C_Mutex_New();
	p->Timers = C_List_New();
	p->Delay = 100;
}

void
C_Timer_Manager_Cleanup(C_Timer_Manager_Ptr p)
{
	C_Mutex_Free(p->Mutex);
	C_List_Free(p->Timers);
	C_Thread_Base_Cleanup(&p->Thread);
}

void
C_Timer_Manager_Run(C_Thread_Ptr _p)
{
	C_Timer_Manager_Ptr p;

	p = (C_Timer_Manager_Ptr)_p;

	while (_p->Run)
	{
		C_Mutex_Aquire(p->Mutex);

		C_Timer_Manager_Process(p);

		C_Mutex_Release(p->Mutex);

		C_Sleep(p->Delay);
	}
}

void
C_Timer_Manager_Lock(C_Timer_Manager_Ptr p)
{
	C_Mutex_Aquire(p->Mutex);
}

void
C_Timer_Manager_Unlock(C_Timer_Manager_Ptr p)
{
	C_Mutex_Release(p->Mutex);
}

C_Error_t
C_Timer_Manager_Start(C_Timer_Manager_Ptr p, C_Timer_Ptr t)
{
	C_Timer_Ptr tc;

	if (p == NULL || t == NULL)
		return C_ERR_INVALID_PARAMS;

	if (p->Id == C_TIMER_ID_INVALID)
		p->Id++;

	t->Id = p->Id++;

	tc = C_Timer_New_From(t);

	return C_Timer_Manager_Schedule(p, tc);
}

C_Error_t
C_Timer_Manager_Stop(C_Timer_Manager_Ptr p, C_Timer_Id_t id)
{
	C_List_Node_Ptr node, tmp;
	C_Timer_Ptr t;

	if (p == NULL)
		return C_ERR_INVALID_PARAMS;

	node = p->Timers->Head;

	while (node != NULL)
	{
		t = (C_Timer_Ptr)node->Data;

		if (t->Id == id)
		{
			tmp = node->Next;
			C_List_Remove(p->Timers, node, NULL);
			node = tmp;
			continue;
		}
		node = node->Next;
	}

	return C_ERR_NOERR;
}

C_Error_t
C_Timer_Manager_Schedule(C_Timer_Manager_Ptr p, C_Timer_Ptr t)
{
	C_List_Node_Ptr node;
	C_Timer_Ptr tmp;

	if (p == NULL || t == NULL)
		return C_ERR_INVALID_PARAMS;

	gettimeofday(&t->Timestamp, NULL);

	t->Timestamp.tv_sec += t->Duration/1000;
	t->Timestamp.tv_usec += t->Duration * 1000 + (t->Duration/1000) * 1000000;
	if (t->Timestamp.tv_usec >= 1000000)
	{
		t->Timestamp.tv_sec++;
		t->Timestamp.tv_usec -= 1000000;
	}

	for (node = p->Timers->Head; node; node = node->Next)
	{
		tmp = (C_Timer_Ptr)node->Data;

		if (tmp->Timestamp.tv_sec < t->Timestamp.tv_sec)
			continue;

		if (tmp->Timestamp.tv_sec == t->Timestamp.tv_sec &&
				tmp->Timestamp.tv_usec < tmp->Timestamp.tv_usec)
			continue;
		break;
	}

	C_List_Insert(p->Timers, t, node);

	return C_ERR_NOERR;
}

void
C_Timer_Manager_Process(C_Timer_Manager_Ptr p)
{
	C_List_Node_Ptr node;
	C_Timer_Ptr t;
	struct timeval tv;

	if (p == NULL)
		return;

	node = p->Timers->Head;

	while (node != NULL)
	{
		t = (C_Timer_Ptr)node->Data;

		gettimeofday(&tv, NULL);

		if ((tv.tv_sec > t->Timestamp.tv_sec) ||
				(tv.tv_sec == t->Timestamp.tv_sec && tv.tv_usec > t->Timestamp.tv_usec))
		{
			if (t->Handler != NULL)
				t->Handler(t);

			if (t->Periodic)
				C_Timer_Manager_Schedule(p, t);

			C_List_Remove(p->Timers, node, NULL);

			node = p->Timers->Head;
		}
		else
		{
			break;
		}
	}
}

void
C_Sleep(long ms)
{
	usleep(ms * 1000);
}
